using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Linq;

namespace FrameIdentityReq.Network
{
    public class AsyncSocket : IDisposable
    {
        public int Port { get; }
        public static int BufferSize { get; private set; }
        byte[] Buffer;

        Socket aSocket;

        readonly object DisposeLock;
        bool IsClosed = false;

        /// <summary>
        /// 接收数据完成.
        /// </summary>
        public event EventHandler<SocketState> OnReceived;

        /// <summary>
        /// 错误事件
        /// </summary>
        public event EventHandler<SocketErrorState> OnError;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="port">接收和发送端口</param>
        /// <param name="bufferSize">缓冲区最小大小</param>
        public AsyncSocket(int port, int bufferSize)
        {
            Port = port;
            BufferSize = bufferSize;
            Buffer = new byte[BufferSize];

            IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, Port);

            aSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            aSocket.Bind(localEndPoint);
            DisposeLock = new object();
        }

        public void Dispose()
        {
            lock (DisposeLock)
            {
                IsClosed = true;
                aSocket.Close();
            }
        }

        public void StartReceiving()
        {
            SocketState state = new SocketState(this);
            try
            {
                lock (DisposeLock)
                {
                    if (!IsClosed)
                    {
                        aSocket.BeginReceiveFrom(Buffer, 0,
                            BufferSize, SocketFlags.None,
                            ref state.RemoteEndPoint,
                            new AsyncCallback(AsyncCallback_ReceiveFrom), state);
                    }
                }
            }
            catch (SocketException e)
            {
                RaiseExcption(state, e);
            }
        }

        void AsyncCallback_ReceiveFrom(IAsyncResult asyncResult)
        {
            SocketState state = (SocketState)asyncResult.AsyncState;

            int count = 0;
            try
            {
                lock (DisposeLock)
                {
                    if (!IsClosed)
                    {
                        count = aSocket.EndReceiveFrom(
                            asyncResult, ref state.RemoteEndPoint);
                        state.Buffer = new ArraySegment<byte>(Buffer, 0, count).ToArray();
                    }
                }
            }
            catch (SocketException e)
            {
                RaiseExcption(state, e);
            }
            finally
            {
                StartReceiving();
            }

            if (count > 0)
            {
                if (OnReceived != null)
                    OnReceived(this, state);
            }
            else if (!IsClosed)
                RaiseError(state, "接收数据失败");
        }

        public void SendTo(IPAddress remoteIP, byte[] message)
        {
            SendTo(new IPEndPoint(remoteIP, Port), message);
        }

        public void SendTo(EndPoint remoteEndPoint, byte[] buffer)
        {
            SocketState state = new SocketState(this, remoteEndPoint, buffer);
            try
            {
                aSocket.BeginSendTo(state.Buffer, 0,
                    state.Buffer.Length, SocketFlags.None,
                    state.RemoteEndPoint,
                    new AsyncCallback(AsyncCallBack_SendTo), state);
            }
            catch (SocketException e)
            {
                RaiseError(state, e.Message);
            }
        }

        void AsyncCallBack_SendTo(IAsyncResult asyncResult)
        {
            int sendLength = aSocket.EndSendTo(asyncResult);
            SocketState state = (SocketState)asyncResult.AsyncState;
            if (state.Buffer.Length != sendLength) RaiseError(state, "发送数据长度不一致!");
            else if (!asyncResult.IsCompleted) RaiseError(state, "发送数据未完成!");
        }

        void RaiseError(SocketState state, string message)
        {
            if (OnError != null)
                OnError(this,
                new SocketErrorState(state, message));
        }

        void RaiseExcption(SocketState state, Exception e)
        {
            RaiseError(state, "接收数据发生异常" + e.Message);
        }
    }
}
